package com.onlyxiahui.common.dispatcher.impl;

import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.core.MethodParameter;
import org.springframework.core.convert.support.DefaultConversionService;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.onlyxiahui.common.annotation.parameter.Define;
import com.onlyxiahui.common.dispatcher.util.DispatcherJsonUtil;
import com.onlyxiahui.common.message.node.Body;
import com.onlyxiahui.common.message.node.DataNode;
import com.onlyxiahui.common.message.node.Head;
import com.onlyxiahui.framework.action.dispatcher.extend.ActionRequest;
import com.onlyxiahui.framework.action.dispatcher.extend.ActionResponse;
import com.onlyxiahui.framework.action.dispatcher.extend.ArgumentBox;
import com.onlyxiahui.framework.action.dispatcher.extend.MethodArgumentResolver;

/**
 * Date 2019-01-12 21:58:45<br>
 * Description
 * 
 * @author XiaHui<br>
 * @since 1.0.0
 */

public class ServiceMethodArgumentResolver implements MethodArgumentResolver {

	protected final DefaultConversionService conversionService = new DefaultConversionService();
	private String key = "request.message.application/json";

	Map<Class<?>, Boolean> canNotInstanceMap = new HashMap<>();

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(Define.class);
	}

	@Override
	public Object resolveArgument(
			MethodParameter parameter,
			ActionRequest request,
			ActionResponse response,
			ArgumentBox argumentBox) {
		Object object = null;

		Define define = parameter.getParameterAnnotation(Define.class);
		// 获取属定义的名称
		String name = null == define ? null : define.value();

		Type t = parameter.getGenericParameterType();
		Class<?> clazz = parameter.getParameterType();

		// 先从暂存中获取json字符串，如果没有就先将body转为json字符串存入暂存，避免多次转换
		String json = (String) request.getAttribute(key);
		if (null == json) {
			Object data = request.getData();
			// 获取json数据
			json = getJson(data);
			if (null == json || json.isEmpty()) {
				json = "{}";
			}
			request.addAttribute(key, json);
		}

		boolean isNode = isNode(clazz);
		if (isNode) {
			name = null == name || name.isEmpty() ? getObjectName(clazz) : name;
		}
		Object value = null;
		if (null == name || "".equals(name)) {
			value = JSONObject.parseObject(json, clazz);
		} else {
			value = JSONPath.read(json, name);
		}

		if (value instanceof JSONObject) {
			JSONObject jo = (JSONObject) value;
			if (isCustomMap(clazz)) {
				object = JSONObject.parseObject(jo.toJSONString(), clazz);
			} else {
				object = jo.toJavaObject(t);
			}
		} else if (value instanceof JSONArray) {
			JSONArray ja = (JSONArray) value;
			object = ja.toJavaObject(t);
//			if (isCollection(clazz)) {
//				object = JSONObject.parseObject(ja.toJSONString(), clazz);
//			} else {
//				object = ja.toJavaObject(t);
//			}
		} else if (null != value) {
			if (clazz.isInstance(value)) {
				object = value;
			} else {
				if (conversionService.canConvert(value.getClass(), clazz)) {
					object = conversionService.convert(value, clazz);
				}
			}
		}
		if (null == object) {
			object = getDefaultValue(clazz);
		}
		if (null == object && isCanInstance(clazz) && !canNotInstanceMap.containsKey(clazz)) {
			object = getObject(clazz);
		}
		return object;
	}

	/**
	 * 
	 * Date 2019-01-06 15:22:00<br>
	 * Description 是否是json数据格式
	 * 
	 * @author XiaHui<br>
	 * @param data
	 * @return
	 * @since 1.0.0
	 */
	private String getJson(Object data) {
		String json = (null == data) ? null : data.toString();
		if (DispatcherJsonUtil.maybeJson(json)) {
			return json;
		} else {
			return null;
		}
	}

	/**
	 * 
	 * Date 2019-01-06 14:53:42<br>
	 * Description 是否可以被实例化
	 * 
	 * @author XiaHui<br>
	 * @param classType
	 * @return
	 * @since 1.0.0
	 */
	private boolean isCanInstance(Class<?> classType) {
		boolean isAbstract = Modifier.isAbstract(classType.getModifiers());
		boolean can = true;
		if (classType.isAnnotation()) {
			can = false;
		} else if (classType.isArray()) {
			can = false;
		} else if (classType.isEnum()) {
			can = false;
		} else if (classType.isInterface()) {
			can = false;
		} else if (isAbstract) {
			can = false;
		}
		return can;
	}

	private boolean isNode(Class<?> clazz) {
		boolean isNode = false;
		boolean isHead = Head.class == clazz || Head.class.isAssignableFrom(clazz);
		boolean isBody = Body.class == clazz || Body.class.isAssignableFrom(clazz);
		boolean isDataNode = DataNode.class == clazz || DataNode.class.isAssignableFrom(clazz);
		isNode = isHead || isBody || isDataNode;
		return isNode;
	}

	private String getObjectName(Class<?> clazz) {
		String name = clazz.getSimpleName();
		if (name.length() > 1) {
			name = name.substring(0, 1).toLowerCase() + name.substring(1);
		} else {
			name = name.toLowerCase();
		}
		return name;
	}

	private <T> Object getObject(Class<T> clazz) {
		Object object = null;
		if (isCanInstance(clazz) && !canNotInstanceMap.containsKey(clazz)) {
			try {
				object = clazz.newInstance();
			} catch (InstantiationException | IllegalAccessException e) {
				canNotInstanceMap.put(clazz, false);
			}
		}
		return object;
	}

	/**
	 * 
	 * Date 2019-01-06 14:53:28<br>
	 * Description
	 * 
	 * @author XiaHui<br>
	 * @param clazz
	 * @return
	 * @since 1.0.0
	 */
	@SuppressWarnings("unchecked")
	public <T> T getDefaultValue(Class<T> clazz) {
		Object object = null;
		if (List.class.isAssignableFrom(clazz)) {
			if (isCanInstance(clazz)) {
				object = getObject(clazz);
			} else {
				object = new ArrayList<Object>(0);
			}
		} else if (Map.class.isAssignableFrom(clazz)) {
			if (isCanInstance(clazz)) {
				object = getObject(clazz);
			} else {
				object = new HashMap<Object, Object>(0);
			}
		} else if (Set.class.isAssignableFrom(clazz)) {
			if (isCanInstance(clazz)) {
				object = getObject(clazz);
			} else {
				object = new HashSet<Object>(0);
			}
		} else if (clazz.isArray()) {
			Class<?> componentType = clazz.getComponentType();
			object = Array.newInstance(componentType, 0);
		} else if (clazz == int.class) {
			object = 0;
		} else if (clazz == long.class) {
			object = 0L;
		} else if (clazz == float.class) {
			object = 0.0F;
		} else if (clazz == double.class) {
			object = 0.00D;
		} else if (clazz == byte.class) {
			object = (byte) 0;
		} else if (clazz == char.class) {
			object = '\u0000';
		} else if (clazz == short.class) {
			object = 0;
		} else if (clazz == boolean.class) {
			object = false;
		}
		return ((T) object);
	}

	/**
	 * 
	 * 
	 * 是否自定义实现Map<br>
	 * Date 2019-10-08 17:19:11<br>
	 * 
	 * @param clazz
	 * @return
	 * @since 1.0.0
	 */
	boolean isCustomMap(Class<?> clazz) {
		boolean isMap = Map.class.isAssignableFrom(clazz);
		boolean isSubMap = java.util.HashMap.class == clazz
				|| java.util.Hashtable.class == clazz
				|| java.util.TreeMap.class == clazz
				|| java.util.IdentityHashMap.class == clazz
				|| java.util.WeakHashMap.class == clazz
				|| java.util.LinkedHashMap.class == clazz;
		return isMap && !isSubMap;
	}

	boolean isCollection(Class<?> clazz) {
		return Collection.class.isAssignableFrom(clazz);
	}
}
